#include "hardcodedMode.h"
#include "hardcodedFigure.h"
#include "hardcodedSkin.h"
#include "hardcodedRunAnimation.h"
#include "hardcodedPatternAnimation.h"

#include <spri/data.h>
#include <spri/resources.h>
#include <spri/assets.h>
#include <spri/bindingBuilder.h>
#include <spri/bindings.h>
#include <spri/instanceBuilder.h>
#include <spri/figureInstance.h>
#include <spri/animationInstance.h>
#include <spri/drawContext.h>

#include <imag/data.h>
#include <imag/resourceHandler.h>
#include <imag/resources.h>
#include <imag/assets.h>
#include <imag/assetProcessor.h>

#include <util/indexMap.h>

#include <foun/foun_math.h>
#include <foun/debug.h>

#include <file/accessor.h>

#include <fram/screen.h>

#include <string.h>

/*
 * Public Instance
 */
HardcodedMode::HardcodedMode()
: SpriteViewerMode()
, m_pFigureAsset(NULL)
, m_pSkinAsset(NULL)
, m_pAnimationAsset(NULL)
, m_pBindingBuilder(NULL)
, m_pFigureSkinBinding(NULL)
, m_pAnimationFigureBinding(NULL)
, m_pInstanceBuilder(NULL)
, m_pFigureInstance(NULL)
, m_pAnimationInstance(NULL)
, m_pImageAssetProcessor(NULL)
, m_projectionTransform()
{
}

HardcodedMode::~HardcodedMode()
{
}

void HardcodedMode::Init()
{
	SpriteViewerMode::Init();

	m_pBindingBuilder = m_pAllocator->AllocType<lct::spri::BindingBuilder>();
	m_pBindingBuilder->SetAllocator(m_pAllocator);

	m_pInstanceBuilder = m_pAllocator->AllocType<lct::spri::InstanceBuilder>();
	m_pInstanceBuilder->SetAllocator(m_pAllocator);

	m_pImageAssetProcessor = m_pAllocator->AllocType<lct::imag::AssetProcessor>();
	m_pImageAssetProcessor->SetAllocator(m_pAllocator);
	m_pImageAssetProcessor->SetResourceHandler(m_pImageResourceHandler);

	BuildFigureAssets();
	BuildSkinAssets();
	BuildAnimationAssets();
	BuildBindings();
	BuildInstances();
}

void HardcodedMode::AcquireGraphics()
{
	for (u32 stripIndex = 0; stripIndex < m_pSkinAsset->pSkinData->stripCount; ++stripIndex)
	{
		lct::spri::SkinAsset::Strip& strip = m_pSkinAsset->pStripArray[stripIndex];
		lct::imag::TextureTableAsset* pTextureTableAsset = strip.pTextureTableAsset;

		lct::imag::TextureTableData* pTextureTableData = pTextureTableAsset->pTextureTableData;
		lct::imag::TextureData textureData;
		textureData.width = pTextureTableData->width;
		textureData.height = pTextureTableData->height;
		textureData.size = textureData.width * textureData.height * sizeof(lct::imag::TexelRGBAData);

		void* pTextureBinary = pTextureTableAsset->pTextureTableBinary;
		for (u32 textureIndex = 0; textureIndex < pTextureTableData->count; ++textureIndex)
		{
			lct::imag::TextureResource* pTextureResource = pTextureTableAsset->pTextureResourceArray + textureIndex;
			m_pImageResourceHandler->AcquireTextureResource(pTextureResource, textureData.width, textureData.height, pTextureBinary);
			pTextureBinary = reinterpret_cast<void*>(reinterpret_cast<u32>(pTextureBinary) + textureData.size);
		}
	}
}

void HardcodedMode::ReleaseGraphics()
{
	for (u32 stripIndex = 0; stripIndex < m_pSkinAsset->pSkinData->stripCount; ++stripIndex)
	{
		lct::spri::SkinAsset::Strip& strip = m_pSkinAsset->pStripArray[stripIndex];
		lct::imag::TextureTableAsset* pTextureTableAsset = strip.pTextureTableAsset;

		lct::imag::TextureTableData* pTextureTableData = pTextureTableAsset->pTextureTableData;

		for (u32 textureIndex = 0; textureIndex < pTextureTableData->count; ++textureIndex)
		{
			lct::imag::TextureResource* pTextureResource = pTextureTableAsset->pTextureResourceArray + textureIndex;
			m_pImageResourceHandler->ReleaseTextureResource(pTextureResource);
		}
	}
}

bool addColor = true;
f32 colorValue = 0.0f;
f32 frames = 0.0;
f32 colorStep = 1.0f / 60.0f;

void HardcodedMode::Update()
{
	if (addColor)
	{
		colorValue += colorStep;
		frames += 1.0f;
		if (frames >= 60.0f)
		{
			colorValue = 1.0f;
			addColor = false;
			frames = 0.0f;
		}
	}
	else
	{
		colorValue -= colorStep;
		frames += 1.0f;
		if (frames >= 60.0f)
		{
			colorValue = 0.0f;
			addColor = true;
			frames = 0.0f;
		}
	}
	//m_clearColor.r = colorValue;

	const lct::foun::RectEdges& screenEdges = m_pScreen->GetRectEdges();
	lct::foun::Matrix44OrthographicProjection(m_projectionTransform, screenEdges.left, screenEdges.right, screenEdges.bottom, screenEdges.top, -1.0f, 1.0f);

	m_pAnimationInstance->UpdateTracks(1.0f);
}

void HardcodedMode::Draw()
{
	m_pFigureInstance->CalculateTextures();
	m_pFigureInstance->CalculateTransforms();

	m_pSpriteDrawContext->ActivateRenderState();
	m_pSpriteDrawContext->ActivateShader();
	m_pSpriteDrawContext->ActivateQuad();
	m_pSpriteDrawContext->ActivateProjectionTransform(m_projectionTransform);
	m_pSpriteDrawContext->DrawFigure(*m_pFigureInstance);
}

/*
 * Private Instance
 */
void HardcodedMode::BuildFigureAssets()
{
	m_pFigureAsset = HardcodedFigure::BuildAsset(m_pAllocator);
}

void HardcodedMode::BuildSkinAssets()
{
	m_pSkinAsset = HardcodedSkin::BuildAsset(m_pAllocator);
}

void HardcodedMode::BuildAnimationAssets()
{
	//m_pAnimationAsset = HardcodedRunAnimation::BuildAsset(m_pAllocator);
	m_pAnimationAsset = HardcodedPatternAnimation::BuildAsset(m_pAllocator);
	FixupAnimationAsset(*m_pAnimationAsset);
}

void HardcodedMode::BuildBindings()
{
	m_pFigureSkinBinding = m_pBindingBuilder->CreateFigureSkinBinding(*m_pFigureAsset, *m_pSkinAsset);
	m_pAnimationFigureBinding = m_pBindingBuilder->CreateAnimationFigureBinding(*m_pAnimationAsset, *m_pFigureAsset);
}

void HardcodedMode::BuildInstances()
{
	m_pFigureInstance = m_pInstanceBuilder->CreateFigureInstance(*m_pFigureAsset);
	m_pFigureInstance->BindSkinAsset(*m_pSkinAsset, *m_pFigureSkinBinding);

	m_pAnimationInstance = m_pInstanceBuilder->CreateAnimationInstance(m_pAnimationAsset->pAnimationData->trackCount);
	m_pAnimationInstance->BindFigureInstance(*m_pFigureInstance);
	m_pAnimationInstance->BindAnimationAsset(*m_pAnimationAsset, *m_pAnimationFigureBinding);
}

void HardcodedMode::FixupAnimationAsset(lct::spri::AnimationAsset& animationAsset)
{
	lct::spri::AnimationData* pAnimationData = animationAsset.pAnimationData;
	lct::spri::AnimationAsset::Track* pTrackArray = animationAsset.pTrackArray;
	for (u32 trackIndex = 0; trackIndex < pAnimationData->trackCount; ++trackIndex)
	{
		lct::spri::AnimationAsset::Track* pTrack = pTrackArray + trackIndex;
		lct::spri::TrackData* pTrackData = pTrack->pTrackData;
		lct::spri::KeyData* pKeyDataArray = pTrack->pKeyDataArray;
		for (u32 keyIndex = 0; keyIndex < pTrackData->keyCount; ++keyIndex)
		{
			lct::spri::KeyData* pCurrKeyData = pKeyDataArray + keyIndex;
			if (keyIndex < (pTrackData->keyCount - 1))
			{
				lct::spri::KeyData* pNextKeyData = pKeyDataArray + keyIndex + 1;
				if (pCurrKeyData->curveType == lct::spri::CURVE_TYPE_LINEAR)
				{
					f32 timeDelta = pNextKeyData->frame - pCurrKeyData->frame;
					f32 valueDelta = pNextKeyData->value - pCurrKeyData->value;
					pCurrKeyData->slope = valueDelta / timeDelta;
				}
			}
		}
	}
}
